/*
Designer   : Renyangang

Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at

	http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package protocol

import (
	"fpga-vboard/logger"
	"sync"
	"time"
)

type ParityType string
type UARTStatus int
type UARTDirection int

const (
	UARTDirectionRx UARTDirection = iota
	UARTDirectionTx
)

const (
	UARTStatusIdle UARTStatus = iota
	UARTStatusStart
	UARTStatusData
	UARTStatusParity
	UARTStatusStop
)

const (
	ParityNone ParityType = "none"
	ParityOdd  ParityType = "odd"
	ParityEven ParityType = "even"
)

type UARTConfig struct {
	// 串口名称
	Name string
	// 波特率
	BaudRate int `label:"BaudRate" required:"true" type:"number" min:"1" unit:"bits/s"`
	// 数据位
	DataBits int `label:"DataBits" required:"true" type:"number" min:"5" max:"8" unit:"bits"`
	// 停止位
	StopBits int `label:"StopBits" required:"true" type:"number" min:"0" max:"2" unit:"bits"`
	// 校验位
	Parity ParityType `label:"Parity" required:"true" type:"select" options:"none,odd,even"`
	// 始终来源
	UseGlobalClock bool `label:"UseGlobalClock" required:"true" type:"onoff"`
}

type UARTStatusMechine struct {
	Config *UARTConfig
	// realtimer
	SampleDuration time.Duration
	BitDuration    time.Duration
	nextBitTime    time.Time
	nextSampleTime time.Time
	// Main Clock counter
	SampleTimerCountNumber uint64
	BitTimerCountNumber    uint64
	sampleTimerConter      uint64
	bitTimerCounter        uint64
	BitCounter             int
	Direction              UARTDirection
	Status                 UARTStatus
	Sig                    bool
	data                   byte
	parityBit              byte
	IsRunning              bool
	curBitPos              int
	samplingDataT          int
	samplingDataF          int
	stopCounter            int
	curParity              byte
	newDataFlag            bool
	readChan               chan byte
	sendChan               chan byte
}

type UART struct {
	Config     *UARTConfig
	RxStatus   *UARTStatusMechine
	TxStatus   *UARTStatusMechine
	dataMutex  *sync.Mutex
	dataBuffer []byte
	ReadChan   chan byte
	SendChan   chan byte
}

func NewUARTStatusMechine(config *UARTConfig, direction UARTDirection) *UARTStatusMechine {
	return &UARTStatusMechine{
		Config:            config,
		SampleDuration:    ((time.Second / time.Duration(config.BaudRate)) + 1) / 8, // 8 倍采样率
		BitDuration:       (time.Second / time.Duration(config.BaudRate)) + 1,
		Direction:         direction,
		Status:            UARTStatusIdle,
		Sig:               true,
		newDataFlag:       false,
		sampleTimerConter: 0,
		bitTimerCounter:   0,
	}
}

func (mechine *UARTStatusMechine) SetSendChan(c chan byte) {
	if mechine.Direction != UARTDirectionTx {
		logger.ErrorLog("UARTStatusMechine: SetSendChan called on non-tx mechine")
		return
	}
	mechine.sendChan = c
}

func (mechine *UARTStatusMechine) SetReadChan(c chan byte) {
	if mechine.Direction != UARTDirectionRx {
		logger.ErrorLog("UARTStatusMechine: SetReadChan called on non-rx mechine")
		return
	}
	mechine.readChan = c
}

func (mechine *UARTStatusMechine) SendNewDataToBuffer() {
	if mechine.newDataFlag {
		select {
		case mechine.readChan <- mechine.data:
			mechine.newDataFlag = false
		default:

		}
	}
}

func (mechine *UARTStatusMechine) CalcParity() byte {
	count := 0
	for i := 0; i < mechine.Config.DataBits; i++ {
		if (mechine.data & (1 << i)) != 0 {
			count++
		}
	}
	if (mechine.Config.Parity == ParityOdd && count%2 == 0) || (mechine.Config.Parity == ParityEven && count%2 != 0) {
		return 1
	} else {
		return 0
	}
}

func (mechine *UARTStatusMechine) SetData(b byte) {
	mechine.data = b
	mechine.parityBit = mechine.CalcParity()
}

func (mechine *UARTStatusMechine) Sampling() {
	if mechine.Direction == UARTDirectionRx && mechine.waitSampleTimer() {
		if mechine.Sig {
			mechine.samplingDataT++
		} else {
			mechine.samplingDataF++
		}
	}
}

func (mechine *UARTStatusMechine) ResetTimers() {
	mechine.bitTimerCounter = 0
	mechine.sampleTimerConter = 0
	mechine.nextBitTime = time.UnixMicro(0)
	mechine.nextSampleTime = time.UnixMicro(0)
}

func (mechine *UARTStatusMechine) waitBitTimer() bool {
	if mechine.Config.UseGlobalClock {
		mechine.bitTimerCounter++
		if mechine.bitTimerCounter >= mechine.BitTimerCountNumber {
			mechine.bitTimerCounter = 0
			return true
		}
	} else {
		if mechine.nextBitTime.Before(time.UnixMicro(100)) {
			// first initialization
			mechine.nextBitTime = time.Now().Add(mechine.BitDuration)
		}
		if mechine.nextBitTime.Before(time.Now()) {
			mechine.nextBitTime = time.Now().Add(mechine.BitDuration)
			return true
		}
	}
	return false
}

func (mechine *UARTStatusMechine) waitSampleTimer() bool {
	if mechine.Config.UseGlobalClock {
		mechine.sampleTimerConter++
		if mechine.sampleTimerConter >= mechine.SampleTimerCountNumber {
			mechine.sampleTimerConter = 0
			return true
		}
	} else {
		if mechine.nextSampleTime.Before(time.UnixMicro(100)) {
			// first initialization
			mechine.nextSampleTime = time.Now().Add(mechine.SampleDuration)
		}
		if mechine.nextSampleTime.Before(time.Now()) {
			mechine.nextSampleTime = time.Now().Add(mechine.SampleDuration)
			return true
		}
	}
	return false
}

func (mechine *UARTStatusMechine) Run() {
	switch mechine.Status {
	case UARTStatusIdle:
		if mechine.Direction == UARTDirectionRx {
			mechine.SendNewDataToBuffer()
			if !mechine.newDataFlag && !mechine.Sig {
				mechine.Status = UARTStatusStart
				mechine.ResetTimers()
				mechine.data = 0
			}
		} else {
			select {
			case d := <-mechine.sendChan:
				mechine.SetData(d)
				mechine.Status = UARTStatusStart
				mechine.ResetTimers()
			default:
				mechine.Sig = true
			}
		}
	case UARTStatusStart:
		if mechine.waitBitTimer() {
			mechine.Status = UARTStatusData
		}
		if mechine.Direction == UARTDirectionTx {
			mechine.Sig = false
		} else {
			mechine.samplingDataF = 0
			mechine.samplingDataT = 0
		}
		mechine.curBitPos = 0
	case UARTStatusData:
		if mechine.waitBitTimer() {
			if mechine.Direction == UARTDirectionRx && mechine.samplingDataT > mechine.samplingDataF {
				mechine.data |= 1 << mechine.curBitPos
			} else if mechine.Direction == UARTDirectionTx {
				mechine.Sig = (mechine.data & (1 << mechine.curBitPos)) != 0
			}
			mechine.samplingDataF = 0
			mechine.samplingDataT = 0
			mechine.curBitPos++
			if mechine.curBitPos >= mechine.Config.DataBits {
				if mechine.Config.Parity != ParityNone {
					mechine.Status = UARTStatusParity
				} else if mechine.Config.StopBits > 0 {
					mechine.Status = UARTStatusStop
					mechine.stopCounter = 0
					mechine.newDataFlag = true
				} else {
					mechine.Status = UARTStatusIdle
					mechine.newDataFlag = true
				}
			}
		}
		mechine.Sampling()
	case UARTStatusParity:
		if mechine.waitBitTimer() {
			if mechine.Direction == UARTDirectionRx {
				if mechine.samplingDataT > mechine.samplingDataF {
					mechine.parityBit = 1
				} else {
					mechine.parityBit = 0
				}
				mechine.curParity = mechine.CalcParity()
				if mechine.parityBit != mechine.curParity {
					// mechine.Status = UARTStatusIdle
					logger.ErrorLog("parity error")
				}
			} else {
				mechine.Sig = mechine.parityBit != 0
			}
			mechine.samplingDataF = 0
			mechine.samplingDataT = 0
			if mechine.Config.StopBits > 0 {
				mechine.Status = UARTStatusStop
				mechine.stopCounter = 0
			} else {
				mechine.Status = UARTStatusIdle
			}
			mechine.newDataFlag = true
		}
		mechine.Sampling()
	case UARTStatusStop:
		if mechine.waitBitTimer() {
			if mechine.Direction == UARTDirectionRx {
				mechine.Sig = true
			}
			mechine.stopCounter++
			if mechine.stopCounter >= mechine.Config.StopBits {
				mechine.Status = UARTStatusIdle
				mechine.stopCounter = 0
			}
		}
		mechine.SendNewDataToBuffer()
	}
}

func NewUART(config *UARTConfig) *UART {
	uart := &UART{
		Config:     config,
		RxStatus:   NewUARTStatusMechine(config, UARTDirectionRx),
		TxStatus:   NewUARTStatusMechine(config, UARTDirectionTx),
		ReadChan:   make(chan byte, 128),
		SendChan:   make(chan byte, 128),
		dataMutex:  &sync.Mutex{},
		dataBuffer: make([]byte, 0),
	}
	uart.RxStatus.SetReadChan(uart.ReadChan)
	uart.TxStatus.SetSendChan(uart.SendChan)
	return uart
}

func (uart *UART) SetMainClockFrequency(freq uint64) {
	uart.RxStatus.BitTimerCountNumber = (freq * 2) / uint64(uart.Config.BaudRate)
	uart.RxStatus.SampleTimerCountNumber = uart.RxStatus.BitTimerCountNumber / 8
}

/***
 *  Uart run , input param rx : signal value for rx
 *  return bool : signal value for tx
 */
func (uart *UART) Run(rx bool) bool {
	uart.RxStatus.Sig = rx
	uart.RxStatus.Run()
	uart.TxStatus.Run()
	return uart.TxStatus.Sig
}
